package zxj.weixin.mp.service;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import zxj.weixin.mp.api.AccessTokenApi;
import zxj.weixin.mp.api.JsSdkApi;
import zxj.weixin.mp.api.WebAuthApi;
import zxj.weixin.mp.domain.Account;
import zxj.weixin.utils.RedisStringUtils;
import zxj.weixin.utils.RedisUtils;

/**
 * 微信公众号基础功能Service
 * 
 * @author zhuxuejiang
 *
 */
@Service
public class WeixinService {
	private static final Logger LOGGER = LoggerFactory.getLogger(WeixinService.class);

	private static final String KEY_ACCESS_TOKEN = "wx_access_token.";
	private static final long TIMEOUT_ACCESS_TOKEN = 60 * 60;

	private static final String KEY_JSAPI_TICKET = "wx_jsapi_ticket.";
	private static final long TIMEOUT_JSAPI_TICKET = 60 * 60;

	private static final String KEY_OAUTH2_ACCESS_TOKEN = "wx_oauth2_access_token.";
	private static final long TIMEOUT_OAUTH2_ACCESS_TOKEN = 60 * 60;

	@Autowired
	private RedisStringUtils redisStringUtils;

	@Autowired
	private RedisUtils<String, Object> redisUtils;

	@Autowired
	private AccountService accountService;

	/**
	 * 获取access_token
	 * 
	 * @param appId 公众号的唯一标识
	 * @return accessToken 公众号的全局唯一接口调用凭据
	 * @throws Exception
	 */
	public String getAccessToken(String appId) throws Exception {
		String key = KEY_ACCESS_TOKEN + appId;

		if (redisStringUtils.hasKey(key)) {
			return redisStringUtils.opsForValueGet(key);
		}

		Account account = accountService.getByAppId(appId);
		if (null == account) {
			LOGGER.error("获取access_token错误： Account is null !");
			return null;
		}

		Map<String, Object> map = AccessTokenApi.getAccessToken(account.getAppId(), account.getAppSecret());
		if (null == map) {
			LOGGER.error("获取access_token错误!");
			LOGGER.error(account.toString());
			return null;
		}

		String accessToken = (String) map.get("access_token");
		if (null == accessToken) {
			LOGGER.error("获取access_token错误!");
			LOGGER.error(account.toString());
			return null;
		}

		redisStringUtils.opsForValueSet(key, accessToken, TIMEOUT_ACCESS_TOKEN);
		return accessToken;
	}

	/**
	 * 获取jsapi_ticket
	 * 
	 * @param appId       公众号的唯一标识
	 * @param accessToken 公众号的全局唯一接口调用凭据
	 * @return jsapiTicket 微信JS接口的临时票据
	 * @throws Exception
	 */
	public String getJsapiTicket(String appId, String accessToken) throws Exception {
		String key = KEY_JSAPI_TICKET + appId;

		if (redisStringUtils.hasKey(key)) {
			return redisStringUtils.opsForValueGet(key);
		}

		Map<String, Object> map = JsSdkApi.getJsapiTicket(appId, accessToken);
		if (null == map) {
			LOGGER.error("获取jsapi_ticket错误!");
			return null;
		}

		String jsapiTicket = (String) map.get("ticket");
		if (null == jsapiTicket) {
			LOGGER.error("获取jsapi_ticket错误!");
			return null;
		}

		redisStringUtils.opsForValueSet(key, jsapiTicket, TIMEOUT_JSAPI_TICKET);
		return jsapiTicket;
	}

	/**
	 * 通过code换取网页授权access_token
	 * 
	 * @param appId 公众号的唯一标识
	 * @param code  换取网页授权access_token的票据
	 * @return 接口返回的数据包
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getWebAuthAccessToken(String appId, String code) throws Exception {

		String key = KEY_OAUTH2_ACCESS_TOKEN + appId + "." + code;
		if (redisUtils.hasKey(key)) {
			return (Map<String, Object>) redisUtils.opsForValueGet(key);
		}

		Account account = accountService.getByAppId(appId);
		if (null == account) {
			LOGGER.error("通过code换取网页授权access_token错误： Account is null !");
			return null;
		}

		Map<String, Object> map = WebAuthApi.getAccessToken(account.getAppId(), account.getAppSecret(), code);
		if (null == map) {
			LOGGER.error("通过code换取网页授权access_token错误!");
			LOGGER.error(account.toString());
			return null;
		}

		String accessToken = (String) map.get("access_token");
		if (StringUtils.isBlank(accessToken)) {
			LOGGER.error("通过code换取网页授权access_token错误!");
			LOGGER.error(account.toString());
			LOGGER.error(map.toString());
			return null;
		}

		redisUtils.opsForValueSet(key, map, TIMEOUT_OAUTH2_ACCESS_TOKEN);
		return map;
	}

}
